package states.gameplay;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import javax.print.attribute.standard.MediaSize.ISO;

import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.slick.state.transition.FadeInTransition;
import org.newdawn.slick.state.transition.FadeOutTransition;

import resources.ResourceManager;
import states.States;
import states.gameplay.Game.State;
import states.gameplay.ShaporGrid.ShaporLink;
import states.gameplay.animations.AttackAnimation;
import states.gameplay.animations.GranadorAttackAnimation;
import states.gameplay.animations.LasorAttackAnimation;
import states.gameplay.shapors.Base;
import states.gameplay.shapors.Defender;
import states.gameplay.shapors.EnemyCreep;
import states.gameplay.shapors.EnemyCreepGenerator;
import states.gameplay.shapors.Energor;
import states.gameplay.shapors.Expandor;
import states.gameplay.shapors.HeavyDefender;
import states.gameplay.shapors.LasorDefender;
import states.gameplay.shapors.GranadorDefender;
import states.gameplay.shapors.Shapor;
import states.gameplay.shapors.ShaporFactory;
import states.gameplay.shapors.ShaporFactory.Type;
import states.gameplay.shapors.Storagor;
import states.gameplay.shapors.Wallor;
import states.mainmenu.MainMenuGameState;

import com.corvo.pulsor.Assets;

public class GameplayState  extends BasicGameState{

	private static final String NOT_ENOUGH_ENERGY = "Not enough energy";
	private int _id;
	private ResourceManager _rm;
	
	private Player _player;
	private Enemy  _enemy;
	
	// images
	private Image _background;
	private Image _action_bar;
	private Image _play_grid;
	
	private Image _baseCreep;
	private Image _regularCreep;
	private Image _enemyCreep;
	private Image _forceField;
	// grid
	private ShaporGrid _grid;
	// icons
	private Image _expandorDeployIcon;
	private Image _energorDeployIcon;
	private Image _storagorDeployIcon;
	private Image _lightDefenderDeployIcon;
	private Image _mediumDefenderDeployIcon;
	private Image _heavyDefenderDeployIcon;
	private Image _wallorDeployIcon;
	
	private int finalTimer;
	
	private Image _win;
	private Image _lose;
	// state
	private Game _game;
	private ShaporFactory _shaporFactory;
	
	private List<AttackAnimation> _animations;
	
	
	public GameplayState(int id) {
		_id = id;
	}
	
	public void initStateForGameplay(Player player, Enemy enemy) {
		_player = player;
		
		_enemy = enemy;
		
		_game = new Game();
		
		_shaporFactory = new ShaporFactory(_rm);
		
		finalTimer = 5000;
	}
	
	@Override
	public int getID() {
		return _id;
	}
	
	public void setResourceManager(ResourceManager rm) {
		_rm = rm;
	}

	@Override
	public void init(GameContainer gc, StateBasedGame sbg) throws SlickException {
		_background = _rm.getImage(Assets.ORANGE_BACKGROUND.getId());
		_action_bar = _rm.getImage(Assets.ACTION_BAR.getId());
		_play_grid  = _rm.getImage(Assets.PLAY_GRID.getId());
		
		_regularCreep = _rm.getImage(Assets.CREEP_REGULAR.getId());
		_baseCreep = _rm.getImage(Assets.CREEP_BASE.getId());
		_enemyCreep = _rm.getImage(Assets.CREEP_ENEMY.getId());
		_forceField = _rm.getImage(Assets.ENERGY_FIELD.getId());
		//
		_expandorDeployIcon = _rm.getImage(Assets.ACTOR_EXPANDOR.getId());
		_energorDeployIcon  = _rm.getImage(Assets.ACTOR_ENERGOR.getId());
		_storagorDeployIcon = _rm.getImage(Assets.ACTOR_STORAGOR.getId());
		_wallorDeployIcon   = _rm.getImage(Assets.ACTOR_WALLOR.getId());
		// defenders
		_lightDefenderDeployIcon  = _rm.getImage(Assets.ACTOR_LIGHT_DEFENDER.getId());
		_mediumDefenderDeployIcon = _rm.getImage(Assets.ACTOR_MEDIUM_DEFENDER.getId());
		_heavyDefenderDeployIcon  = _rm.getImage(Assets.ACTOR_HEAVY_DEFENDER.getId());
		
		_win = _rm.getImage(Assets.WIN.getId());
		_lose = _rm.getImage(Assets.LOSE.getId());
		
		_animations = new ArrayList<AttackAnimation>();
	}
	
	@Override
	public void enter(GameContainer gc, StateBasedGame sbg) throws SlickException {
		_game.setCurrentState(State.INIT);
		_grid = new ShaporGrid();
		
		// set up the player stuff
		Position pos = _player.getBasePosition();
		Base base = _player.getBase();
		
		_grid.deployShapor(base, pos.x, pos.y);
		
		// set up the enemy stuff
		List<Position> positions = _enemy.getPositions();
		List<EnemyCreepGenerator> creeps = _enemy.getGenerators();
		
		for(int idx = 0; idx < creeps.size(); idx++){
			_grid.deployShapor(creeps.get(idx), positions.get(idx).x, positions.get(idx).y);
		}
	}

	@Override
	public void render(GameContainer gc, StateBasedGame sbg, Graphics g) throws SlickException {
		// draw the background
		_background.draw();
		
		if(_game.getCurrentState() != null){
			switch (_game.getCurrentState()) {
			case INIT:
				// draw the action_bar
				_action_bar.draw();
				break;
			case PLAY:
				renderPlay(gc, sbg, g);
				break;
			case WIN:
				renderWin(gc, sbg, g);
				break;
			case LOSE:
				renderLose(gc, sbg, g);
				break;
			default:
				break;
			}
			
			renderErrorMessage(g, gc.getInput().getAbsoluteMouseX() - 20, gc.getInput().getAbsoluteMouseY() - 20);
		}
		// debug grid
		//_play_grid.draw(0, 24);
	}

	private void renderLose(GameContainer gc, StateBasedGame sbg, Graphics g) {
		_lose.draw(350, 250);
	}

	private void renderWin(GameContainer gc, StateBasedGame sbg, Graphics g) {
		_win.draw(350, 250);
	}

	private void renderPlay(GameContainer gc, StateBasedGame sbg, Graphics g) {
		
		
		int startY = 24;
		
		for(int x = 0; x < ShaporGrid.GRID_WIDTH; x++){
			for(int y = 0; y < ShaporGrid.GRID_HEIGHT; y++){
				Shapor shapor = _grid._grid[x][y];
				// render the creep
				if(shapor != null){
					if(shapor instanceof Base || shapor instanceof Expandor){
						if(shapor.getCurrentState() == Shapor.State.DEPLOYED){
							_baseCreep.setAlpha(1.0f);
						}else {
							_baseCreep.setAlpha(0.3f);
						}
						_baseCreep.draw(x * Game.CELL_SIZE - (_baseCreep.getWidth() - Game.CELL_SIZE)/2, startY + y * Game.CELL_SIZE - (_baseCreep.getHeight() - Game.CELL_SIZE)/2);
					}else 
					if(shapor instanceof EnemyCreepGenerator || shapor instanceof EnemyCreep ){
						_enemyCreep.draw(x * Game.CELL_SIZE - (_enemyCreep.getWidth()-Game.CELL_SIZE)/2, startY + y * Game.CELL_SIZE - (_enemyCreep.getHeight() - Game.CELL_SIZE)/2);
					}else
					{
						if(shapor.getCurrentState() == Shapor.State.DEPLOYED){
							_regularCreep.setAlpha(1.0f);
						}else {
							_regularCreep.setAlpha(0.3f);
						}
						_regularCreep.draw(x * Game.CELL_SIZE - (_regularCreep.getWidth()-Game.CELL_SIZE)/2, startY + y * Game.CELL_SIZE - (_regularCreep.getHeight() - Game.CELL_SIZE)/2);
					}
				}
			}
		}
		// render shields
		for(Wallor wallor : _player.getWallors()){
			Position pos = _grid.getPositionFor(wallor);
			
			if(wallor.getCurrentState() == Shapor.State.DEPLOYED){
				_forceField.draw(pos.x*Game.CELL_SIZE+10 - (_forceField.getWidth())/2, pos.y*Game.CELL_SIZE+10+24 - (_forceField.getHeight())/2);
			}

		}
		
		// render links
		List<ShaporLink> links =  _grid.getAllLinks();
		float oldLineWidth = g.getLineWidth();
		Color oldColor = g.getColor();
		
		g.setLineWidth(6);
		g.setColor(Color.black);
		
		for(ShaporLink link: links){
			g.drawLine(link.pos1.x*20+10, link.pos1.y*20+24+10, link.pos2.x*20+10, link.pos2.y*20+24+10);
		}
		
		g.setLineWidth(oldLineWidth);
		g.setColor(oldColor);
		
		
		for(int x = 0; x < ShaporGrid.GRID_WIDTH; x++){
			for(int y = 0; y < ShaporGrid.GRID_HEIGHT; y++){
				Shapor shapor = _grid._grid[x][y];
				// render the sahpor
				if(shapor != null){
					//x * cell_size - (render.getWidth()+cell_size)/2, startY + y * cell_size - (render.getHeight() + cell_size)/2
					shapor.render(g, x * Game.CELL_SIZE + Game.CELL_SIZE/2, startY + y * Game.CELL_SIZE + Game.CELL_SIZE/2);
				}
			}
		}
		
		// render the mouse action
		if(_game.getCurrentAction() != null){
			Position mouse = _game.getMouseCoords();
			Image selectedIcon = null;
			switch(_game.getCurrentAction()){
			case DEPLOY_EXPANDOR:
				selectedIcon =_expandorDeployIcon;
				break;
			case DEPLOY_ENERGOR:
				selectedIcon = _energorDeployIcon;
				break;
			case DEPLOY_STORAGOR:
				selectedIcon = _storagorDeployIcon;
				break;
			case DEPLOY_WALLOR:
				selectedIcon = _wallorDeployIcon;
				break;
			case DEPLOY_LIGHT_DEFENDER:
				selectedIcon = _lightDefenderDeployIcon;
				break;
			case DEPLOY_MEDIUM_DEFENDER:
				selectedIcon = _mediumDefenderDeployIcon;
				break;
			case DEPLOY_HEAVY_DEFENDER:
				selectedIcon = _heavyDefenderDeployIcon;
				break;
			}
			
			if(selectedIcon != null){
				selectedIcon.draw(mouse.x+10, mouse.y+10, 0.7f);
			}
			
		}
		
		for(AttackAnimation animation : _animations){
			animation.render(g);
		}
		
		// draw the action_bar
		_action_bar.draw();
		
		// render the pulse
		//FIXME : change these strings
		String pulseStr = String.valueOf(_game.getTimeToNextPulse());
		g.drawString(pulseStr, 740, 1);
		
		// draw energy consuption
		g.drawString(_player.getLastEnergyGeneration() + "/" + _player.getLastEnergyConsumption(), 860, 2);
		
		// draw the energy
		g.drawString( _player.getCurrentEnergy() + "/" + _player.getMaximumEnergy() , 960, 2);
		
		
	}

	@Override
	public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException {
		if(_game.getCurrentState() != null){
			switch (_game.getCurrentState()) {
			case INIT:
				_game.setCurrentState(State.PLAY);
				break;
			case PLAY:
				updatePlay(gc, sbg, delta);
				break;
			case LOSE:
				updateLose(gc, sbg, delta);
				break;
			case WIN:
				updateWin(gc, sbg, delta);
				break;
				
			default:
				break;
			}
			
			updateErrorMessage(delta);
		}
	}

	private void updateWin(GameContainer gc, StateBasedGame sbg, int delta) {
		finalTimer -= delta;
		
		if(finalTimer < 0){
			MainMenuGameState mm = (MainMenuGameState) sbg.getState(States.MAIN_MENU_ID);
			sbg.enterState(States.MAIN_MENU_ID, new FadeOutTransition(Color.black, 1000), new FadeInTransition(Color.black, 1000));
		}
	}

	private void updateLose(GameContainer gc, StateBasedGame sbg, int delta) {
		finalTimer -= delta;
		
		if(finalTimer < 0){
			MainMenuGameState mm = (MainMenuGameState) sbg.getState(States.MAIN_MENU_ID);
			sbg.enterState(States.MAIN_MENU_ID, new FadeOutTransition(Color.black, 1000), new FadeInTransition(Color.black, 1000));
		}
	}

	private void updatePlay(GameContainer gc, StateBasedGame sbg, int delta) {
		
		// update animations
		List<AttackAnimation> overs = new ArrayList<AttackAnimation>();
		
		for(AttackAnimation animation : _animations){
			animation.update(delta);
			
			if(animation.isOver()){
				overs.add(animation);
			}
		}
		
		for(AttackAnimation animation : overs){
			_animations.remove(animation);
			Position creepPos = animation.getEndPos();
			
			if(animation.getRadiusOfEffect() == 0){
				_enemy.getCreep().remove(_grid._grid[creepPos.x][creepPos.y]);
				_grid.removeShaporAt(creepPos);
			} else {
				// FIXME: HURGHHHHH, UGLEH
				List<Position> posList = _grid.getRadiusPositions(animation.getEndPos(), animation.getRadiusOfEffect());
				
				for(Position pos : posList){
					
					Shapor shapor = _grid._grid[pos.x][pos.y];
					
					if(shapor instanceof EnemyCreep){
						_grid.removeShapor(shapor);
						_enemy.getCreep().remove(shapor);
					} else if(shapor instanceof EnemyCreepGenerator) {
						_grid.removeShapor(shapor);
						_enemy.getGenerators().remove(shapor);
					}
				}
			}
			
			//defender.setTimeToNextAttack(2000);
//			Shapor shapor = _grid._grid[animation.getStartPos().x][animation.getStartPos().y];
//			if(shapor instanceof LasorDefender){
//				((LasorDefender) shapor).setTimeToNextAttack(1000);
//			} else
//			if(shapor instanceof GranadorDefender){
//				((GranadorDefender) shapor).setTimeToNextAttack(2000);
//			}
		}
		
		Input input = gc.getInput();
		// obtain the mouse input
		// get the mouse coords
		int mouseX = input.getAbsoluteMouseX();
		int mouseY = input.getAbsoluteMouseY();
		_game.setMouseCoords(mouseX, mouseY);
		
		// check if action was performed
		if(input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)){
			_game.setCurrentAction(null);
		}else
		if(input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)){
			if( mouseY < 24 ){
				// in action bar
				final int option = mouseX / 35 ; // size of each button
				switch(option){
				case 0:
					_game.setCurrentAction(Game.Action.DEPLOY_EXPANDOR);
					break;
				case 1:
					_game.setCurrentAction(Game.Action.DEPLOY_STORAGOR);
					break;
				case 2:
					_game.setCurrentAction(Game.Action.DEPLOY_ENERGOR);
					break;
				case 3:
					_game.setCurrentAction(Game.Action.DEPLOY_WALLOR);
					break;
				case 4:
					_game.setCurrentAction(Game.Action.DEPLOY_LIGHT_DEFENDER);
					break;
				case 5:
					_game.setCurrentAction(Game.Action.DEPLOY_MEDIUM_DEFENDER);
					break;
				case 6:
					_game.setCurrentAction(Game.Action.DEPLOY_HEAVY_DEFENDER);
					break;
				}
			}else{
				// gameplay board
				if(_game.getCurrentAction() != null){
					switch(_game.getCurrentAction()){
					
					case DEPLOY_EXPANDOR:
						deployExpandor(mouseX, mouseY);
						break;
					case DEPLOY_ENERGOR:
						deployEnergor(mouseX, mouseY);
						break;
					case DEPLOY_STORAGOR:
						deployStoragor(mouseX, mouseY);
					case DEPLOY_WALLOR:
						deployWallor(mouseX, mouseY);
						break;
					case DEPLOY_LIGHT_DEFENDER:
						deployLasorDefender(mouseX, mouseY);
						break;
					case DEPLOY_MEDIUM_DEFENDER:
						deployGranadorDefender(mouseX, mouseY);
						break;
					case DEPLOY_HEAVY_DEFENDER:
						deployHeavyDefender(mouseX, mouseY);
						break;
					}
				}
			}
		}
		
		performDefense(delta);
		
		_game.removeTimeToPulse(delta);
		
		if(_game.getTimeToNextPulse() == 0){
			// perform pulse
			performPulse(gc, sbg, delta);			
			_game.resetPulse();
		}
		for (EnemyCreep creep : _enemy.getCreep()){
			creep.update(delta);
		}
		
		// winning condition
		if(_enemy.getGenerators().size() == 0){
			_game.setCurrentState(Game.State.WIN);
		}
	}

	private void performDefense(int delta) {
		for(Defender defender : _player.getDefenders()){
			defender.updateTimeToNexAttack(delta);
			
			if(defender.getTimeToNextAttack() == 0){
				if(defender instanceof LasorDefender){
					performLasorAttack((LasorDefender)defender);
					// FIXME : must remove these hardcoded stuff
					defender.setTimeToNextAttack(1000);
				}else 				
				if(defender instanceof GranadorDefender){
					performGranadorAttack((GranadorDefender)defender);
					
					defender.setTimeToNextAttack(3000);
				}
			}
		}
	}

	private void performGranadorAttack(GranadorDefender granador) {
		// select one target
		Position lasorPos = _grid.getPositionFor(granador);
		
		Position enemyCreepPos = _grid.getRandomPositionForEnemyRadius(lasorPos, granador.getAttackRadius());
		
		if(enemyCreepPos != null){
			GranadorAttackAnimation animation = new GranadorAttackAnimation(lasorPos, enemyCreepPos);
			_animations.add(animation);
		}
	}

	private void performLasorAttack(LasorDefender lasor) {
		// select one target
		Position lasorPos = _grid.getPositionFor(lasor);
		
		if(lasorPos == null) {
			//System.out.println("Da fuq?");
			return;
		}
		
		Position enemyCreepPos = _grid.getRandomPositionForEnemyRadius(lasorPos, lasor.getAttackRadius());
		
		if(enemyCreepPos != null){
			LasorAttackAnimation animation = new LasorAttackAnimation(lasorPos, enemyCreepPos);
			_animations.add(animation);
		}
	}

	private void deployWallor(int mouseX, int mouseY) {
		if (!_player.canSpend( _shaporFactory.getCost(Type.WALLOR) ) ){
			// show no energy error
			showErrorMessage(NOT_ENOUGH_ENERGY);
			return;
		}else{
			Position pos = GameUtils.getGridPositionFromCoords(mouseX, mouseY, Game.GRID_DISPLACEMENT, Game.CELL_SIZE);
			
			Wallor wallor = _shaporFactory.generateWallor();
			
			if(pos != null){
				List<Shapor> allShapors = _grid.getAllShapors();
				boolean added = false;
				for(Shapor shapor : allShapors){
					if(shapor.getCurrentState() == Shapor.State.DEPLOYED &&_grid.isInRadius(_grid.getPositionFor(shapor), pos, shapor.getDeployLimits())){
						if(!added){
							_grid.deployShapor(wallor, pos.x, pos.y);
							_player.removeFromCurrentEnergy(wallor.getCost());
							_player.getWallors().add(wallor);
						}
						addLinkInGrid(shapor, wallor);
					}
				}
			}
		}
		
		_game.setCurrentAction(null);
	}

	private void deployGranadorDefender(int mouseX, int mouseY) {
		if (!_player.canSpend( _shaporFactory.getCost(Type.HEAVY_DEFENDER) ) ){
			// show no energy error
			showErrorMessage(NOT_ENOUGH_ENERGY);
			return;
		}else{
			Position pos = GameUtils.getGridPositionFromCoords(mouseX, mouseY, Game.GRID_DISPLACEMENT, Game.CELL_SIZE);
			
			GranadorDefender mediumDefender = _shaporFactory.generateMediumDefender();
			
			if(pos != null){
				List<Shapor> allShapors = _grid.getAllShapors();
				boolean added = false;
				for(Shapor shapor : allShapors){
					if(shapor.getCurrentState() == Shapor.State.DEPLOYED &&_grid.isInRadius(_grid.getPositionFor(shapor), pos, shapor.getDeployLimits())){
						if(!added){
							_grid.deployShapor(mediumDefender, pos.x, pos.y);
							_player.removeFromCurrentEnergy(mediumDefender.getCost());
							_player.getDefenders().add(mediumDefender);
						}
						
						addLinkInGrid(shapor, mediumDefender);
					}
				}
			}
		}
		
		_game.setCurrentAction(null);
	}
	
	private void deployHeavyDefender(int mouseX, int mouseY) {
		if (!_player.canSpend( _shaporFactory.getCost(Type.MEDIUM_DEFENDER) ) ){
			// show no energy error
			showErrorMessage(NOT_ENOUGH_ENERGY);
			return;
		}else{
			Position pos = GameUtils.getGridPositionFromCoords(mouseX, mouseY, Game.GRID_DISPLACEMENT, Game.CELL_SIZE);
			
			HeavyDefender heavyDefender = _shaporFactory.generateHeavyDefender();
			
			if(pos != null){
				List<Shapor> allShapors = _grid.getAllShapors();
				boolean added = false;
				for(Shapor shapor : allShapors){
					if(shapor.getCurrentState() == Shapor.State.DEPLOYED &&_grid.isInRadius(_grid.getPositionFor(shapor), pos, shapor.getDeployLimits())){
						if(!added){
							_grid.deployShapor(heavyDefender, pos.x, pos.y);
							_player.removeFromCurrentEnergy(heavyDefender.getCost());
							_player.getDefenders().add(heavyDefender);
						}
						
						addLinkInGrid(shapor, heavyDefender);
					}
				}
			}
		}
		
		_game.setCurrentAction(null);
	}

	private void deployLasorDefender(int mouseX, int mouseY) {
		if (!_player.canSpend( _shaporFactory.getCost(Type.LIGHT_DEFENDER) ) ){
			// show no energy error
			showErrorMessage(NOT_ENOUGH_ENERGY);
			return;
		}else{
			Position pos = GameUtils.getGridPositionFromCoords(mouseX, mouseY, Game.GRID_DISPLACEMENT, Game.CELL_SIZE);
			
			LasorDefender lightDefender = _shaporFactory.generateLightDefender();
			
			if(pos != null){
				List<Shapor> allShapors = _grid.getAllShapors();
				boolean added = false;
				for(Shapor shapor : allShapors){
					if(shapor.getCurrentState() == Shapor.State.DEPLOYED && _grid.isInRadius(_grid.getPositionFor(shapor), pos, shapor.getDeployLimits())){
						if(!added){
							_grid.deployShapor(lightDefender, pos.x, pos.y);
							_player.removeFromCurrentEnergy(lightDefender.getCost());
							_player.getDefenders().add(lightDefender);
						}
						
						addLinkInGrid(shapor, lightDefender);
					}
				}
			}
		}
		
		_game.setCurrentAction(null);
	}

	private void addLinkInGrid(Shapor added, Shapor toAdd) {
		if(!((added instanceof EnemyCreep || added instanceof EnemyCreepGenerator)) ){
			_grid.addLink(added, toAdd);
		}		
	}

	private void deployStoragor(int mouseX, int mouseY) {
		if (!_player.canSpend( _shaporFactory.getCost(Type.STORAGOR) ) ){
			// show no energy error
			showErrorMessage(NOT_ENOUGH_ENERGY);
			return;
		}else{
			Position pos = GameUtils.getGridPositionFromCoords(mouseX, mouseY, Game.GRID_DISPLACEMENT, Game.CELL_SIZE);
			
			Storagor storagor = _shaporFactory.generateStoragor();
			
			if(pos != null){
				List<Shapor> allShapors = _grid.getAllShapors();
				boolean added = false;
				for(Shapor shapor : allShapors){
					if(shapor.getCurrentState() == Shapor.State.DEPLOYED && _grid.isInRadius(_grid.getPositionFor(shapor), pos, shapor.getDeployLimits())){
						if(!added){
							_grid.deployShapor(storagor, pos.x, pos.y);
							_player.removeFromCurrentEnergy(storagor.getCost());
						}
						addLinkInGrid(shapor, storagor);
					}
				}
			}
		}
		
		_game.setCurrentAction(null);
	}

	private void deployEnergor(int mouseX, int mouseY) {
		if (!_player.canSpend( _shaporFactory.getCost(Type.ENERGOR) ) ){
			// show no energy error
			showErrorMessage(NOT_ENOUGH_ENERGY);
			return;
		}else{
			Position pos = GameUtils.getGridPositionFromCoords(mouseX, mouseY, Game.GRID_DISPLACEMENT, Game.CELL_SIZE);
			
			Energor energor = _shaporFactory.generateEnergor();
			
			if(pos != null){
				List<Shapor> allShapors = _grid.getAllShapors();
				boolean added = false;
				for(Shapor shapor : allShapors){
					if(shapor.getCurrentState() == Shapor.State.DEPLOYED && _grid.isInRadius(_grid.getPositionFor(shapor), pos, shapor.getDeployLimits())){
						if(!added){
							added = true;
							_grid.deployShapor(energor, pos.x, pos.y);
							_player.removeFromCurrentEnergy(energor.getCost());
						}
						addLinkInGrid(shapor, energor);
					}
				}
			}
		}
		
		_game.setCurrentAction(null);
	}

	/** DEPLOY STUFF ON BOARD **/
	private void deployExpandor(int mouseX, int mouseY) {
		
		if (!_player.canSpend( _shaporFactory.getCost(Type.EXPANDOR) ) ){
			// show no energy error
			showErrorMessage(NOT_ENOUGH_ENERGY);
			return;
		}else{
			Position pos = GameUtils.getGridPositionFromCoords(mouseX, mouseY, Game.GRID_DISPLACEMENT, Game.CELL_SIZE);
			
			Expandor expandor = _shaporFactory.generateExpandor();
			
			if(pos != null){
				List<Shapor> allShapors = _grid.getAllShapors();
				boolean added = false;
				for(Shapor shapor : allShapors){
					if(shapor.getCurrentState() == Shapor.State.DEPLOYED && _grid.isInRadius(_grid.getPositionFor(shapor), pos, shapor.getDeployLimits())){
						if(!added){
							_grid.deployShapor(expandor, pos.x, pos.y);
							_player.removeFromCurrentEnergy(expandor.getCost());
						}
						addLinkInGrid(shapor, expandor);
					}
				}
			}
			
			
		}
		
		_game.setCurrentAction(null);
	}

	private void performPulse(GameContainer gc, StateBasedGame sbg, int delta) {
		// generate enemy creep
		Set<Position> newCreep = generateNewCreep();
				
		int energyGenerated = 0;
		int energyConsumed = 0;
		
		List<Shapor> allShapors = _grid.getAllShapors();
		//FIXME :IIIIIIIIIIIIIIFFFFFFFFFFFFFFFFFSSSSS
		for(Shapor shapor : allShapors){
			if(shapor instanceof EnemyCreep || shapor instanceof EnemyCreepGenerator){
				// still not sure
			}else {
				if(shapor.getCurrentState() == Shapor.State.BUILDING){
					shapor.decreaseDeployPulses();
					// if counter == 0, deploy effective shapor
					if(shapor.getDeployPulses() == 0){
						if(shapor instanceof Storagor){
							Storagor storagor = (Storagor)shapor;
							_player.setMaximumEnergy(_player.getMaximumEnergy() + storagor.getStorage());
						}
					}
				}else{
					energyGenerated += shapor.getEnergyGenerated();
					// consume one energy per active shapor
					// TODO: Enable this
					energyConsumed += shapor.getEnergyUpkeep();
				}
			}
		}
		
		_player.setLastEnergyConsumption(energyConsumed);
		_player.setLastEnergyGeneration(energyGenerated);
		// its should always be 1
		int energy = energyGenerated-energyConsumed;
		energy = energy <= 0 ? 1 : energy;
		// update the energy
		_player.addToCurrentEnergy(energy);
	}

	// for faster access, static stuff
	private static List<Position> creepGenerationPos = new ArrayList<Position>();
	{
		creepGenerationPos.add(new Position(0, 1));
		creepGenerationPos.add(new Position(-1, 0));
		creepGenerationPos.add(new Position(1, 0));
		creepGenerationPos.add(new Position(0, -1));
	}

	private Set<Position> generateNewCreep() {
		Random r = new Random();
		
		//FIXME: Do not generate every time the pulse is called 
//		List<EnemyCreep> newCreeps = new ArrayList<EnemyCreep>();
		Set<Position> newCreepsPos = new HashSet<Position>();
		
		List<Position> _walledPositions = getWallPositions();
		
		// generate creeps along the creep
		for(EnemyCreep creep : _enemy.getCreep()){
			Position pos = _grid.getPositionFor(creep);
			
			Position temp = new Position();
			for(Position creepGenPos : creepGenerationPos){
				temp.x = creepGenPos.x + pos.x;
				temp.y = creepGenPos.y + pos.y;
				
				if(temp.x < 0 || temp.x >= ShaporGrid.GRID_WIDTH ||
						temp.y < 0 || temp.y >= ShaporGrid.GRID_HEIGHT){
					continue;
				}
				// check for wallors
				if( creep.getLevel() <= _player.getDefenseLevel() && _walledPositions.contains(temp) ){
					continue;
				}
				
				boolean isCreep = ((_grid._grid[temp.x][temp.y] instanceof EnemyCreep) || (_grid._grid[temp.x][temp.y] instanceof EnemyCreepGenerator)); 
				
				int rand = r.nextInt(3);
				if( rand < 1 && !isCreep ){
					newCreepsPos.add(new Position(temp.x, temp.y));
				}
			}

		}
		
		// generate creep at generators
		for(EnemyCreepGenerator generator : _enemy.getGenerators()){
			Position pos = _grid.getPositionFor(generator);
			
			// verify the 9 places around the generator
			int minX = pos.x == 0 ? pos.x : pos.x-1;
			int minY = pos.y == 0 ? pos.y : pos.y-1;
			int maxX = pos.x == ShaporGrid.GRID_WIDTH-1 ? pos.x: pos.x+1;
			int maxY = pos.y == ShaporGrid.GRID_HEIGHT-1 ? pos.y: pos.y+1;
			
			for(int x = minX; x <= maxX; x++){
				for(int y = minY; y <= maxY; y++){
					int rand = r.nextInt(3);
					if( rand < 1 && _grid._grid[x][y] == null ){
						newCreepsPos.add(new Position(x, y));
					}
				}
			}
		}
		
		for(Position pos : newCreepsPos) {
			Shapor shapor = _grid._grid[pos.x][pos.y];
			
			if(shapor != null){
				if(shapor instanceof Defender){
					_player.getDefenders().remove((Defender)shapor);
				} else 
				if(shapor instanceof Wallor){
					_player.getWallors().remove(shapor);
				} else 
				if(shapor instanceof Storagor){
					_player.setMaximumEnergy(_player.getMaximumEnergy() - ((Storagor)shapor).getStorage());
				} else
					// Loosing condition
				if(shapor instanceof Base){
					_game.setCurrentState(Game.State.LOSE);
				}
				//remove the shapor
				_grid.removeShapor(shapor);
			}
			
			EnemyCreep creep = _shaporFactory.getEnemyCreep();
			_enemy.getCreep().add(creep);
			_grid.deployShapor(creep, pos.x, pos.y);
		}
		
		return newCreepsPos;
	}


	private List<Position> getWallPositions() {

		List<Position> positions = new ArrayList<Position>();
		
		for(Wallor wallor : _player.getWallors()){
			positions.addAll(_grid.getRadiusPositions( _grid.getPositionFor(wallor), wallor.getDefenseRadius() ));
		}
		
		return positions;
	}

	private String _errorMessage;
	private int _errorTimer;
	private void showErrorMessage(String msg) {
		_errorMessage = msg;
		_errorTimer = 3000;
	}
	
	private void renderErrorMessage(Graphics g, int x, int y){
		if(_errorMessage != null){
			g.drawString(_errorMessage, x, y);
		}
	}
	
	private void updateErrorMessage(int delta){
		if(_errorMessage != null){
			_errorTimer -= delta;
			if(_errorTimer < 0){
				_errorTimer = 0;
				_errorMessage = null;
			}
		}
	}

	
}
